/*************************************************************************
*  Unterfunktione fuer UART
*  -Original von Peter Fleury-
*
*  Beschreibung:
*  Ist eine UART Uebertragung beendet wird ein Interrupt ausgeloest. Die ISR
*  nutzt Ring-Puffer zur Speicherung der Daten
*
*  contact: sebastian.trommer@gmail.com
*  homepage: wiki.car2.de
*
*  Revision History
*
*  when         what  who	why
*
*  2009-01-05   1.00  TSE	initial release
*
***************************************************************************/

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include "uart.h"


// Konstanten und Makros
/* RX/TX Puffer */
#define UART_RX_BUFFER_MASK ( UART_RX_BUFFER_SIZE - 1)
#define UART_TX_BUFFER_MASK ( UART_TX_BUFFER_SIZE - 1)

#define ATMEGA_USART0
#define UART0_RECEIVE_INTERRUPT   SIG_USART_RECV
#define UART0_TRANSMIT_INTERRUPT  SIG_USART_DATA
#define UART0_STATUS   UCSR0A
#define UART0_CONTROL  UCSR0B
#define UART0_DATA     UDR0
#define UART0_UDRIE    UDRIE0

// Globale Variablen
static volatile unsigned char UART_TxBuf[UART_TX_BUFFER_SIZE];
static volatile unsigned char UART_RxBuf[UART_RX_BUFFER_SIZE];
static volatile unsigned char UART_TxHead;
static volatile unsigned char UART_TxTail;
static volatile unsigned char UART_RxHead;
static volatile unsigned char UART_RxTail;
static volatile unsigned char UART_LastRxError;


ISR(UART0_RECEIVE_INTERRUPT)
/*************************************************************************
Funktion: UART hat Daten empfangen
Verhalten:  Aufruf wenn die UART ein Zeichen empfangen hat
**************************************************************************/
{
    unsigned char tmphead;
    unsigned char data;
    unsigned char usr;
    unsigned char lastRxError;

    /* lies UART Status Register und UART Daten Register */
    usr  = UART0_STATUS;
    data = UART0_DATA;

    lastRxError = (usr & (_BV(FE0)|_BV(DOR0)) );

    /* Puffer index berechnen */
    tmphead = ( UART_RxHead + 1) & UART_RX_BUFFER_MASK;

    if ( tmphead == UART_RxTail ) {
        /* FEHLER: Ueberlauf des Empfangspuffers */
        lastRxError = UART_BUFFER_OVERFLOW >> 8;
    }else{
        /* speichere neuen Index */
        UART_RxHead = tmphead;
        /* speichere Daten im Puffer */
        UART_RxBuf[tmphead] = data;
    }
    UART_LastRxError = lastRxError;
}


ISR(UART0_TRANSMIT_INTERRUPT)
/*************************************************************************
Funktion: UART Daten Register ist leer
Verhalten:  Aufruf wenn die UART bereit ist neue Daten zu senden
**************************************************************************/
{
    unsigned char tmptail;


    if ( UART_TxHead != UART_TxTail) {
        /* berechne und speicher neune Pufferindex */
        tmptail = (UART_TxTail + 1) & UART_TX_BUFFER_MASK;
        UART_TxTail = tmptail;
        /* hole ein Byte aus dem Puffer und schreibe es ins UART */
        UART0_DATA = UART_TxBuf[tmptail];  /* start Uebertragung */
    }else{
        /* tx Puffer leer, UDRE Interrupt ausschalten*/
        UART0_CONTROL &= ~_BV(UART0_UDRIE);
    }
}


/*************************************************************************
Funktion: UART_init()
Verhalten:  Initialisiert das UART und setzt die BAUDRATE
Parameter:  Baudrate von Makro UART_BAUD_SELECT()
Rueckgabe:  none
**************************************************************************/
void UART_init(unsigned int baudrate)
{
    UART_TxHead = 0;
    UART_TxTail = 0;
    UART_RxHead = 0;
    UART_RxTail = 0;

    /* Setze BAUDRATE */
    if ( baudrate & 0x8000 )
    {
   		UART0_STATUS = (1<<U2X0);  //Enable 2x speed
   		baudrate &= ~0x8000;
   	}
    UBRR0H = (unsigned char)(baudrate>>8);
    UBRR0L = (unsigned char) baudrate;

    /* USART Empfaenger und Sender sowie Empfangsinterrupt aktivieren */
    UART0_CONTROL = _BV(RXCIE0)|(1<<RXEN0)|(1<<TXEN0);

    /* Setze Format: asynchron, 8data, no parity, 1stop bit */
    #ifdef URSEL0
    UCSR0C = (1<<URSEL0)|(3<<UCSZ00);
    #else
    UCSR0C = (3<<UCSZ00);
    #endif

}/* uart_init */


/*************************************************************************
Funktion: UART_getc()
Verhalten:  liefert Byte aus Ringpuffer
Rueckgabe:  low byte:  empfangenes Byte vom ringbuffer
          high byte: letzter Empfangsfehler
**************************************************************************/
unsigned int UART_getc(void)
{
    unsigned char tmptail;
    unsigned char data;


    if ( UART_RxHead == UART_RxTail ) {
        return UART_NO_DATA;   /* keine Daten vorhanden */
    }

    /* berechne/speichere Puffer Index */
    tmptail = (UART_RxTail + 1) & UART_RX_BUFFER_MASK;
    UART_RxTail = tmptail;

    /* hole Daten aus Puffer */
    data = UART_RxBuf[tmptail];

    return (UART_LastRxError << 8) + data;

}/* uart_getc */


/*************************************************************************
Funktion: UART_putc()
Verhalten:  schreibe Byte in Ringpuffer
Parameter:  zu uebertragendes Byte
Rueckgabe:  none
**************************************************************************/
void UART_putc(unsigned char data)
{
    unsigned char tmphead;


    tmphead  = (UART_TxHead + 1) & UART_TX_BUFFER_MASK;

    while ( tmphead == UART_TxTail ){
        ;/* warte das Platz im Puffer frei wird */
    }

    UART_TxBuf[tmphead] = data;
    UART_TxHead = tmphead;

    /* aktiviere UDRE Interrupt */
    UART0_CONTROL    |= _BV(UART0_UDRIE);

}/* uart_putc */


/*************************************************************************
Funktion: UART_puts()
Verhalten:  Uebertrage Zeichenkette zur UART
Parameter:  die Zeichenkette
Rueckgabe:  none
**************************************************************************/
void UART_puts(unsigned char *s )
{
    while (*s)
      UART_putc(*s++);

}/* uart_puts */


/*************************************************************************
Funktion: UART_puts_p()
Verhalten:  Uebertrage Zeichenkette aus dem Programmspeicher zur UART
Parameter:  die betreffende Zeichenkette
Rueckgabe:  none
**************************************************************************/
void UART_puts_p(const char *progmem_s )
{
    register char c;

    while ( (c = pgm_read_byte(progmem_s++)) )
      UART_putc(c);

}/* uart_puts_p */


